﻿using System.Collections.Generic;
using FITClub.Episode1.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FITClub.Episode1.Levels
{
    /// <summary>
    /// This class is game component which represents deathly fluid at the bottom of the screen
    /// </summary>
    public class FluidDeath : DrawableGameComponent, IConfigComponent
    {
        #region FIELDS

        /// <summary>
        /// Settings of this fluid loaded from level settings
        /// </summary>
        public FluidDeathConfig Config { get; private set; }

        /// <summary>
        /// Basic model for the fluit - normally just a plane
        /// </summary>
        public Model Water { get; private set; }

#if WINDOWS || XBOX

        /// <summary>
        /// Custom shader designed to animate the fluid
        /// </summary>
        public Effect effect { get; private set; }

#endif
        /// <summary>
        /// List of textures to be periodically changed on the surface of the fluid
        /// </summary>
        public List<Texture2D> Textures;
        /// <summary>
        /// Fluid motion and position parameters
        /// </summary>
        public float Time = 0, rotationX, rotationLightY, scale, posy, amplitude = 0.005f, factor = 5;
        Matrix World;

        public new GameFITClub Game { get; private set; }

        public double TimeToSwitch = 0;
        public int TextureIndex = 0;

        #endregion FIELDS

        #region METHODS

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="game">Game instance</param>
        /// <param name="config">Fluid settings</param>
        public FluidDeath(GameFITClub game, FluidDeathConfig config)
            : base(game)
        {
            this.Config = config;
            this.Game = game;
        }

        /// <summary>
        /// Allows component to load its resources
        /// </summary>
        protected override void LoadContent()
        {
            base.LoadContent();
            this.ReloadConfig();
        }

        /// <summary>
        /// Allows component to update and draw itself
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            if (this.Water == null) return;

            float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
            this.TimeToSwitch -= time;
            if (this.TimeToSwitch < 0)
            {
                this.TimeToSwitch = this.Config.Period;
                this.TextureIndex++;
                if (this.TextureIndex >= this.Config.TexturePaths.Count) this.TextureIndex = 0;
            }
            this.Game.GraphicsDevice.BlendState = BlendState.Opaque;
            this.Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            Time += (float)(gameTime.ElapsedGameTime.TotalSeconds);
#if WINDOWS || XBOX
            this.effect.Parameters["Time"].SetValue(Time);
#endif
            foreach (ModelMesh mesh in Water.Meshes)
            {
                mesh.Draw();
            }
            base.Draw(gameTime);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        public void UpdateWorld()
        {
            ////
        }

        /// <summary>
        /// Loads fluid reasources, settings and configures fluid's shader
        /// </summary>
        public void ReloadConfig()
        {
            this.UpdateWorld();
            if (this.Config.WaterPath != null && this.Config.WaterPath.Trim() != string.Empty) this.Water = Game.Content.Load<Model>(this.Config.WaterPath);
            else return;
            World = Matrix.CreateRotationY(MathHelper.ToRadians(90)) * Matrix.CreateRotationX(MathHelper.ToRadians(180)) * Matrix.CreateScale(10) * Matrix.CreateTranslation(this.Config.Position);
#if WINDOWS || XBOX
            this.effect = Game.Content.Load<Effect>("Shaders\\CartoonWaterEffect");
            this.Textures = new List<Texture2D>();
            for (int i = 0; i < this.Config.TexturePaths.Count; i++)
            {
                this.Textures.Add(Game.Content.Load<Texture2D>(this.Config.TexturePaths[i].Path));
            }
            this.effect.Parameters["TextureEnabled"].SetValue(false);
            if (this.Textures.Count > 0)
            {
                this.effect.Parameters["Texture"].SetValue(this.Textures[this.TextureIndex]);
                this.effect.Parameters["TextureEnabled"].SetValue(true);
            }
            this.effect.CurrentTechnique = effect.Techniques["Toon"];//["Lambert"];
            this.effect.Parameters["World"].SetValue(World);
            this.effect.Parameters["View"].SetValue(this.Game.ViewMatrix);
            this.effect.Parameters["Projection"].SetValue(this.Game.ProjectionMatrix);
            this.effect.Parameters["WorldViewProjection"].SetValue(World * this.Game.ViewMatrix * this.Game.ProjectionMatrix);
            this.effect.Parameters["Amplitude"].SetValue(amplitude);
            this.effect.Parameters["PeriodX"].SetValue((float)this.Config.Period);
            this.effect.Parameters["PeriodZ"].SetValue((float)this.Config.Period);

            Vector3 light = new Vector3(0, -1, 0);
            this.effect.Parameters["LightDirection"].SetValue(Vector3.Transform(light, Matrix.CreateRotationY(rotationLightY)));

            foreach (ModelMesh mesh in Water.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect = this.effect;
                }
            }
#endif
        }

        #endregion METHODS
    }
}